﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using BizTalkFlow.Model;
using System.Xml;

namespace BizTalkFlow.Render
{

    public class DgmlRender
    {
        private static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
        
        public XmlDocument RenderAsXmlDocument(string templateFielname, ArtefactModel artefactsLinks, bool createApplications, bool createFlows, bool includeFlowInstances)
        {
            var xdoc = Render(templateFielname, artefactsLinks, createApplications, createFlows, includeFlowInstances);

            var xmlDocument = new XmlDocument();
            using (var xmlReader = xdoc.CreateReader())
            {
                xmlDocument.Load(xmlReader);
            }
            return xmlDocument;
        }

        public XDocument Render(string templateFilename, ArtefactModel portsLinksModel, bool createApplications, bool createFlows, bool includeFlowInstances)
        {
            var graph = InitGraph(templateFilename);

            var ns = graph.Root.GetDefaultNamespace();

            var nodes = graph.Root.Element(ns + "Nodes");
            var links = graph.Root.Element(ns + "Links");

            createArtefactNodes(portsLinksModel.Artefacts, ns, nodes);
            createArtefactLinks(portsLinksModel.Links, ns, links);

            if (createFlows)
                createFlowContenaires(portsLinksModel.Links, ns, nodes, links);

            if (createApplications)
                createApplicationContenaires(portsLinksModel.Artefacts, ns, nodes, links);

            if (includeFlowInstances == true)
                createArtefactInstanceLinks(portsLinksModel.LinkInstances, ns, nodes, links);

            return graph;//.Save(this.OutputFulllFilename);
        }

        private void createArtefactNodes(List<Artefact> artefacts, XNamespace ns, XElement nodes)
        {
            foreach (var artefact in artefacts)
            {
                var currentNode = (from el in nodes.Elements()
                                   where (string)el.Attribute("Id") == artefact.Id.ToString()
                                   select el).FirstOrDefault();

                if (currentNode == null)
                {
                    currentNode = new XElement(ns + "Node"
                                                    , new XAttribute("Id", artefact.Id)
                                                    , new XAttribute("Label", artefact.Name)
                                                    , new XAttribute("Category", artefact.Type)
                                                    );
                    nodes.Add(currentNode);
                }
            }
        }

        private void createArtefactLinks(List<ArtefactLink> artefactLinks, XNamespace ns, XElement links)
        {
            foreach (var artefactLink in artefactLinks)
            {
                links.Add(new XElement(ns + "Link"
                                            , new XAttribute("Source", artefactLink.FromComponentId)
                                            , new XAttribute("Target", artefactLink.ToComponentId)
                                            ));
            }
        }

        private void createArtefactInstanceLinks(List<ArtefactLinkInstance> artefactLinkInstances, XNamespace ns, XElement nodes, XElement links)
        {
            foreach (var artefactLink in artefactLinkInstances.OrderBy(t => t.FromComponentId))
            {
                var currentNodeFrom = createInstanceFrom(ns, nodes, links, artefactLink);
                var currentNodeTo = createInstanceTo(ns, nodes, links, artefactLink);

                links.Add(new XElement(ns + "Link"
                                       , new XAttribute("Source", currentNodeFrom.Attribute("Id").Value)
                                       , new XAttribute("Target", currentNodeTo.Attribute("Id").Value)
                                       ));
            }
        }

        private static XElement createInstanceTo(XNamespace ns, XElement nodes, XElement links, ArtefactLinkInstance artefactLink)
        {
            //Add To instance
            var contenaireTo = (from el in nodes.Elements()
                                where (string)el.Attribute("Id") == artefactLink.ToComponentId.ToString()
                                select el).FirstOrDefault();

            if (contenaireTo.Attribute("Group") == null)
                contenaireTo.Add(new XAttribute("Group", "Collapsed"));


            var currentNodeTo = (from el in nodes.Elements()
                                 where (string)el.Attribute("Id") == artefactLink.ToServiceInstanceId.ToString()
                                 select el).FirstOrDefault();

            if (currentNodeTo == null)
                currentNodeTo = new XElement(ns + "Node"
                                , new XAttribute("Id", artefactLink.ToServiceInstanceId)
                                , new XAttribute("Label", "svc " + artefactLink.ToServiceInstanceId)
                                );

            links.Add(new XElement(ns + "Link"
                                         , new XAttribute("Source", contenaireTo.Attribute("Id").Value)
                                         , new XAttribute("Target", currentNodeTo.Attribute("Id").Value)
                                         , new XAttribute("Category", "Contains")
                                         ));
            return currentNodeTo;
        }

        private static XElement createInstanceFrom(XNamespace ns, XElement nodes, XElement links, ArtefactLinkInstance artefactLink)
        {
            //Add From instance
            var contenaireFrom = (from el in nodes.Elements()
                                  where (string)el.Attribute("Id") == artefactLink.FromComponentId.ToString()
                                  select el).FirstOrDefault();

            if (contenaireFrom.Attribute("Group") == null)
                contenaireFrom.Add(new XAttribute("Group", "Collapsed"));


            var currentNodeFrom = (from el in nodes.Elements()
                                   where (string)el.Attribute("Id") == artefactLink.FromServiceInstanceId.ToString()
                                   select el).FirstOrDefault();

            if (currentNodeFrom == null)
                currentNodeFrom = new XElement(ns + "Node"
                                , new XAttribute("Id", artefactLink.FromServiceInstanceId)
                                , new XAttribute("Label", "svc " + artefactLink.FromServiceInstanceId)
                                );

            links.Add(new XElement(ns + "Link"
                                         , new XAttribute("Source", contenaireFrom.Attribute("Id").Value)
                                         , new XAttribute("Target", currentNodeFrom.Attribute("Id").Value)
                                         , new XAttribute("Category", "Contains")
                                         ));
            return currentNodeFrom;
        }

        private void createFlowContenaires(List<ArtefactLink> artefactLinks, XNamespace ns, XElement nodes, XElement links)
        {
            var flowTemplates = (from a in artefactLinks
                                 select a.FowTemplate).Distinct();

            foreach (var flowTemplate in flowTemplates)
            {
                var contenaire = new XElement(ns + "Node"
                                                 , new XAttribute("Id", flowTemplate)
                                                 , new XAttribute("Label", "Flow: " + flowTemplate)
                                                 , new XAttribute("Group", "Collapsed")
                                                 );
                nodes.Add(contenaire);

                var artefacts = (from a in artefactLinks
                                 where a.FowTemplate == flowTemplate
                                 select a);

                foreach (var artefact in artefacts)
                {
                    links.Add(new XElement(ns + "Link"
                                , new XAttribute("Source", contenaire.Attribute("Id").Value)
                                , new XAttribute("Target", artefact.FromComponentId)
                                , new XAttribute("Category", "Contains")
                                ));

                    links.Add(new XElement(ns + "Link"
                                , new XAttribute("Source", contenaire.Attribute("Id").Value)
                                , new XAttribute("Target", artefact.ToComponentId)
                                , new XAttribute("Category", "Contains")
                                ));
                }
            }
        }

        private void createApplicationContenaires(List<Artefact> artefacts, XNamespace ns, XElement nodes, XElement links)
        {
            var applications = (from a in artefacts
                                select new { a.ApplicationId, a.ApplicationName }).Distinct();

            foreach (var application in applications)
            {
                var contenaire = new XElement(ns + "Node"
                                                 , new XAttribute("Id", "Application_" + application.ApplicationId)
                                                 , new XAttribute("Label", application.ApplicationName)
                                                 , new XAttribute("Group", "Collapsed")
                                                 );
                nodes.Add(contenaire);

                var appArtefacts = (from a in artefacts
                                    where a.ApplicationId == application.ApplicationId
                                    select a);

                foreach (var appArtefact in appArtefacts)
                {
                    links.Add(new XElement(ns + "Link"
                                , new XAttribute("Source", contenaire.Attribute("Id").Value)
                                , new XAttribute("Target", appArtefact.Id)
                                , new XAttribute("Category", "Contains")
                                ));
                }
            }
        }

        
        //private static void createMessageNodes(XNamespace ns, XElement nodes, XElement links, ServiceInstance instance, XElement currentNodeInstance)
        //{
        //    XElement previousNodeMessage = null;
        //    foreach (var message in instance.Messages.OrderBy(m => m.Timestamp))
        //    {
        //        var currentNodeMessage = new XElement(ns + "Node"
        //                                , new XAttribute("Id", Guid.NewGuid().ToString()) //message.InstanceID )
        //                                , new XAttribute("SchemaName", message.SchemaName)
        //            //, new XAttribute("StatusId", message.StatusId)
        //                                , new XAttribute("Url", message.Url)
        //                                , new XAttribute("Label", message.EventDirection + ":" + message.SchemaName)
        //                                , new XAttribute("InstanceID", message.InstanceID)
        //                                , new XAttribute("EventId", message.EventId)
        //                                , new XAttribute("Timestamp", message.Timestamp)
        //                                , new XAttribute("EventDirection", message.EventDirection)
        //                                , new XAttribute("Size", (message.Size == null) ? -1 : message.Size)
        //                                , new XAttribute("Category", "Message")
        //                                );

        //        links.Add(new XElement(ns + "Link"
        //                                    , new XAttribute("Source", currentNodeInstance.Attribute("Id").Value)
        //                                    , new XAttribute("Target", currentNodeMessage.Attribute("Id").Value)
        //                                    , new XAttribute("Category", "Contains")
        //                                    ));

        //        var existingMessage = (from el in nodes.Elements()
        //                               where (string)el.Attribute("InstanceID") == (string)currentNodeMessage.Attribute("InstanceID")
        //                               select el).FirstOrDefault();

        //        if (existingMessage != null)
        //        {
        //            if ((DateTime)existingMessage.Attribute("Timestamp") > (DateTime)currentNodeMessage.Attribute("Timestamp"))
        //                links.Add(new XElement(ns + "Link"
        //                                        , new XAttribute("Source", currentNodeMessage.Attribute("Id").Value)
        //                                        , new XAttribute("Target", existingMessage.Attribute("Id").Value)
        //                                        ));
        //            else
        //                links.Add(new XElement(ns + "Link"
        //                                        , new XAttribute("Source", existingMessage.Attribute("Id").Value)
        //                                        , new XAttribute("Target", currentNodeMessage.Attribute("Id").Value)
        //                                        ));
        //        }

        //        nodes.Add(currentNodeMessage);

        //        //lien des messsages par ordrre d'arrivé au sein d'un même service
        //        if (previousNodeMessage != null && (string)previousNodeMessage.Attribute("EventDirection") != "Send")
        //        {
        //            links.Add(new XElement(ns + "Link"
        //                                    , new XAttribute("Source", previousNodeMessage.Attribute("Id").Value)
        //                                    , new XAttribute("Target", currentNodeMessage.Attribute("Id").Value)
        //                                    ));
        //        }
        //        previousNodeMessage = currentNodeMessage;
        //    }
        //}

        private XDocument InitGraph(string templateFilename)
        {

            var graph = XDocument.Load(templateFilename);

            var ns = graph.Root.GetDefaultNamespace();
            var nodes = graph.Root.Element(ns + "Nodes");
            var links = graph.Root.Element(ns + "Links");

            nodes.RemoveAll();
            links.RemoveAll();

            return graph;
        }

    }
}
